package org.multithreadprint;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Copyright (c) 2023-2033 All Rights Reserved
 * 方法 1，推荐
 * 题目：三个线程分别打印 A，B，C，要求这三个线程一起运行，打印 n 次，按顺序输出形如“ABCABCABC....”的字符串。
 * 方法：通过 ReentrantLock 加锁控制并发，定义状态变量控制执行顺序。抢到锁且状态满足才打印，执行打印才改变状态和计数。
 * 实现：
 *      1. 主函数创建三个线程，不同线程执行不同字符任务，提供当前线程绑定的状态值、绑定的要打印的字符
 *      2. 打印函数实现个数控制、加解锁、状态对比、打印
 *      3. 线程同时启动，但只有一个线程可以取到锁
 *      4. 状态变量 % N 线程数 对比线程状态值，符合预期值才打印，状态值 + 1，个数值 + 1，实现打印控制
 *      5. 取到锁的线程，状态不匹配，直接释放锁。只有符合状态的线程才能打印。
 *      6. 打印个数即方法的执行次数，在方法外层加 for 循环控制，不要用循环控制线程执行次数，存在没执行的情况
 *      7. lock() 位置应该在 for 循环内，要控制的是每次循环的打印
 * 细节：
 *      1. ReentrantLock 加锁 lock() 后必须直接 try 防止中间代码异常导致无法释放锁
 *      2. ReentrantLock 解锁 unlock() 必须在 finally 中第一行执行
 *      3. ReentrantLock 加解锁必须成对使用
 *      4. Thread new 完后要启动 .start()
 *
 * @date: 2023/7/14
 */
public class MultiThreadReentrantLock {
    private int times;  // 控制打印次数
    private int status; // 控制执行状态，初始值 0
    private Lock lock = new ReentrantLock();   // 控制并发

    MultiThreadReentrantLock(int times) {
        this.times = times;
    }

    private void printLetter(int flag, String letter) {
        for (int i = 0; i < times; ) {
            lock.lock();
            try {
                if (status % 3 == flag) {
                    System.out.print(letter);
                    i++;
                    status++;
                }
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        MultiThreadReentrantLock multiThreadReentrantLock = new MultiThreadReentrantLock(5);

        new Thread(() -> {
            multiThreadReentrantLock.printLetter(0, "A");
        }).start();

        new Thread(() -> {
            multiThreadReentrantLock.printLetter(1, "B");
        }).start();

        new Thread(() -> {
            multiThreadReentrantLock.printLetter(2, "C");
        }).start();
    }

    /** 只控制执行顺序的写法
     * 通过 ReentrantLock 加锁控制并发，一次只有一个线程执行打印方法
     * 定义 状态变量 控制执行顺序，只有取到锁且状态匹配才打印
     */
    private void printLetter1(int flag, String letter) {
        lock.lock();
        try {
            if (status % 3 == flag) {
                System.out.print(letter);
                status++;
            }
        } finally {
            lock.unlock();
        }
    }
}
